home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
FishMarket 1.0
/
FishMarket v1.0.iso
/
fishies
/
076-100
/
disk_098
/
hddriver
/
driver
/
wd.c
< prev
Wrap
C/C++ Source or Header
|
1992-05-06
|
12KB
|
635 lines
/*
* Copyright 1987 Alan Kent
*
* Permission is granted to redistribute this code as long
* as this message is retained in the code and the code is
* not sold without written permission from the author.
*
* UUCP: {seismo,hplabs,mcvax,ukc,nttlab}!munnari!goanna.oz!ajk
* ACSnet: ajk@goanna.oz
* ARPA: munnari!goanna.oz!ajk@SEISMO.ARPA
*/
#include "hd.h"
#include <hardware/custom.h>
#include <hardware/intbits.h>
extern void rdsec ();
extern void wrsec ();
extern int init_irq ();
extern void free_irq ();
extern void wait_for_irq ();
extern void set_cmd_issued ();
extern ULONG * NewHashTable ();
extern void FreeHashTable ();
extern void AddHashTable ();
extern LONG TestHashTable ();
ULONG * bad_hash_table;
struct {
LONG cmd_issued;
struct Task * task;
LONG sig_bits;
} data;
struct wd1002 {
UBYTE pad0;
UBYTE data;
UBYTE pad1;
UBYTE error;
#define write_precomp error
UBYTE pad2;
UBYTE sec_count;
UBYTE pad3;
UBYTE sec_num;
UBYTE pad4;
UBYTE cyl_low;
UBYTE pad5;
UBYTE cyl_high;
UBYTE pad6;
UBYTE select;
UBYTE pad7;
UBYTE status;
#define cmd_reg status
};
#define WD ((struct wd1002 *)0x9ffff0)
#define WDS_BUSY 0x80
#define WDS_READY 0x40
#define WDS_WRFAULT 0x20
#define WDS_SEEKDONE 0x10
#define WDS_DRQ 0x08
#define WDS_ERRCORRECTED 0x04
#define WDS_ERROR 0x01
#define WDE_BADBLOCK 0x80
#define WDE_CRCERROR 0x40
#define WDE_IDNOTFOUND 0x10
#define WDE_ABORT 0x04
#define WDE_TR0ERROR 0x02
#define WDE_DAMERROR 0x01
#define WDC_RESTORE 0x10
#define WDC_STEPSPEED 0x00
#define WDC_SEEK 0x70
#define WDC_READ 0x20
#define WDC_WRITE 0x30
#define WDC_FORMAT 0x50
#define WDC_MULTIPLE 0x04
/* drive 1, ECC, 512 byte sectors */
#define WD_SELECT 0xa0
/* deselct by selecting drive 2 */
#define WD_DESELECT 0xa8
#define WD_HEAD 0x03
static int wd_head;
int
wd_open ()
{
int status;
char dummy;
struct posn posn;
UBYTE *p;
int i;
/* first make sure its plugged in!!! */
WD->sec_count = 0;
if ( WD->sec_count != 0 )
return ( -1 );
WD->sec_count = 56;
if ( WD->sec_count != 56 )
return ( -1 );
/* initialize hash table for quick bad sector map checking */
bad_hash_table = NewHashTable ( 4000L );
if ( bad_hash_table == NULL )
return ( -1 );
/* initialize interrupts */
if ( init_irq () < 0 ) {
FreeHashTable ( bad_hash_table );
return ( -1 );
}
/* select the drive */
WD->select = WD_SELECT;
busy_wait ();
while ( WD->status & WDS_DRQ ) {
dummy = WD->data;
WD->data = 0;
}
WD->write_precomp = 32;
WD->sec_count = 1;
WD->cyl_low = 0;
WD->cyl_high = 0;
WD->select = WD_SELECT;
wd_head = 0;
status = wd_cmd ( WDC_RESTORE | WDC_STEPSPEED , TRUE );
if ( status == 0 ) {
deselect ();
/* ok, now read in the header sectors */
posn.sector = 0;
posn.block = 0;
posn.surface = 0;
posn.cylinder = 0;
p = (UBYTE*) &first;
status = read_sector ( &posn , p );
if ( status == 0 ) {
if ( first.magic != HD_MAGIC ) {
/* no magic sectors at beginning. default something */
first.sectors = 16;
first.heads = 4;
first.cylinders = 480;
first.bad_sectors = 0;
first.park_cylinder = 512;
first.map_sectors = 0;
}
else {
/* read in rest of first structure */
for ( posn.sector = 1;
posn.sector < first.map_sectors;
posn.sector++ ) {
p += HD_SECTOR;
posn.block = posn.sector;
status = read_sector ( &posn , p );
if ( status != 0 )
break;
}
/* if all ok, set up hash table for bad sectors */
if ( status == 0 ) {
for ( i = 0; i < first.bad_sectors; i++ )
AddHashTable ( bad_hash_table , first.map[i] );
}
}
}
}
if ( status != 0 ) {
free_irq ();
FreeHashTable ( bad_hash_table );
return ( -1 );
}
return ( 0 );
}
void
wd_close ()
{
free_irq ();
FreeHashTable ( bad_hash_table );
}
int
wd_cmd ( cmd , wait )
int cmd;
int wait;
{
register int error;
register int status;
char dummy;
WD->select = WD_SELECT | wd_head;
busy_wait ();
while ( ( WD->status & WDS_SEEKDONE ) == 0 ) ;
set_cmd_issued ();
WD->cmd_reg = cmd;
if ( wait ) {
wait_for_irq ();
}
busy_wait ();
while ( ( WD->status & WDS_SEEKDONE ) == 0 ) ;
error = WD->status;
status = 0;
if ( ( error & WDS_READY ) == 0 )
status = TDERR_NotSpecified;
else if ( error & WDS_WRFAULT )
status = TDERR_WriteProt;
else if ( error & WDS_ERROR ) {
error = WD->error;
if ( error & WDE_BADBLOCK )
status = TDERR_BadSecSum;
else if ( error & WDE_ABORT )
status = TDERR_NotSpecified;
else if ( error & WDE_TR0ERROR )
status = TDERR_SeekError;
else if ( error & WDE_DAMERROR )
status = TDERR_BadSecPreamble;
else if ( error & WDE_IDNOTFOUND )
status = TDERR_NoSecHdr;
else if ( error & WDE_CRCERROR )
status = TDERR_BadSecSum;
else
status = TDERR_NotSpecified;
}
if ( status != 0 ) {
/* try and recover from error - dont leave registers in bad way */
busy_wait ();
while ( WD->status & WDS_DRQ )
dummy = WD->data;
/* force a read of the error register */
error = WD->error;
while ( WD->status & WDS_DRQ )
dummy = WD->data;
/* do a restore too */
WD->select = WD_SELECT;
busy_wait ();
while ( ( WD->status & WDS_SEEKDONE ) == 0 ) ;
set_cmd_issued ();
WD->cmd_reg = WDC_RESTORE;
wait_for_irq ();
busy_wait ();
while ( ( WD->status & WDS_SEEKDONE ) == 0 ) ;
error = WD->status;
}
return ( status );
}
int
wd_seek ( posn )
register struct posn *posn;
{
int status;
WD->select = WD_SELECT;
busy_wait ();
wd_head = posn->surface;
WD->cyl_low = posn->cylinder;
WD->cyl_high = posn->cylinder >> 8;
WD->sec_num = posn->sector;
status = wd_cmd ( WDC_SEEK , TRUE );
if ( status != 0 ) {
bad_error = status;
bad_posn = *posn;
}
else
deselect ();
return ( status );
}
wd_park ()
{
int status;
int dummy;
WD->select = WD_SELECT;
wd_head = 0;
busy_wait ();
WD->cyl_low = first.park_cylinder;
WD->cyl_high = first.park_cylinder >> 8;
WD->sec_num = 0;
status = wd_cmd ( WDC_SEEK , TRUE );
if ( status != 0 && bad_error == 0 ) {
bad_error = status;
bad_posn = cur_posn;
}
/* flush anything else (or if error) */
while ( WD->status & WDS_DRQ )
dummy = WD->data;
/* if parked, dont deselect unless error */
/* (my drive's light turns yellow when parked) */
if ( status != 0 )
deselect ();
return ( status );
}
int
read_sector ( posn , buf )
struct posn *posn;
UBYTE *buf;
{
int status;
int dummy;
cur_posn = *posn;
bad_remap ( &cur_posn );
WD->select = WD_SELECT;
wd_head = cur_posn.surface;
busy_wait ();
WD->cyl_low = cur_posn.cylinder;
WD->cyl_high = cur_posn.cylinder >> 8;
WD->sec_num = cur_posn.sector;
status = wd_cmd ( WDC_READ , TRUE );
if ( status != 0 && bad_error == 0 ) {
bad_error = status;
bad_posn = cur_posn;
}
if ( status == 0 )
rdsec ( buf , &WD->data , (LONG)HD_SECTOR );
/* flush anything else (or if error) */
while ( WD->status & WDS_DRQ )
dummy = WD->data;
deselect ();
return ( status );
}
#ifdef DAM_SECTOR_MAPPING
/* This wont really work because of bad sector remapping */
int
read_track ( posn , buf )
register struct posn *posn;
UBYTE *buf;
{
int status;
int dummy;
int sector;
cur_posn = *posn;
bad_remap ( &cur_posn );
WD->select = WD_SELECT;
wd_head = cur_posn.surface;
busy_wait ();
WD->cyl_low = cur_posn.cylinder;
WD->cyl_high = cur_posn.cylinder >> 8;
for ( sector = 0; sector < first.sectors; sector++ ) {
WD->sec_num = sector;
status = wd_cmd ( WDC_READ , TRUE );
if ( status != 0 && bad_error == 0 ) {
bad_error = status;
bad_posn = cur_posn;
}
if ( status != 0 )
break;
rdsec ( buf , &WD->data , (LONG)HD_SECTOR );
buf += HD_SECTOR;
}
/* flush anything else (or if error) */
while ( WD->status & WDS_DRQ )
dummy = WD->data;
deselect ();
return ( status );
}
#endif
/* this version simply does multiple calls to read_sector() */
/* so the bad sector mapping should be ok */
int
read_track ( posn , buf )
struct posn *posn;
UBYTE *buf;
{
int status;
struct posn save;
int sector;
save = *posn;
for ( save.sector = 0; save.sector < first.sectors; save.sector++ ) {
save.block = save.sector + save.surface * first.sectors
+ save.cylinder * first.heads * first.sectors;
status = read_sector ( &save , buf );
if ( status != 0 )
break;
buf += HD_SECTOR;
}
return ( status );
}
int
write_sector ( posn , buf )
register struct posn *posn;
char *buf;
{
int status;
int dummy;
if ( posn->cylinder < 0 ) {
return;
}
cur_posn = *posn;
bad_remap ( &cur_posn );
WD->select = WD_SELECT;
wd_head = cur_posn.surface;
busy_wait ();
WD->cyl_low = cur_posn.cylinder;
WD->cyl_high = cur_posn.cylinder >> 8;
WD->sec_num = cur_posn.sector;
status = wd_cmd ( WDC_WRITE , FALSE );
if ( status != 0 && bad_error == 0 ) {
bad_error = status;
bad_posn = cur_posn;
}
if ( status == 0 )
wrsec ( buf , &WD->data , (LONG)HD_SECTOR );
/* Flush buffer in case of error */
while ( WD->status & WDS_DRQ )
dummy = WD->data;
wait_for_irq ();
deselect ();
return ( status );
}
int
wd_format_track ( posn )
register struct posn *posn;
{
register int i;
int dummy;
WD->sec_num = 0;
WD->cyl_low = posn->cylinder;
WD->cyl_high = posn->cylinder >> 8;
WD->select = WD_SELECT | posn->surface;
WD->sec_count = first.sectors;
set_cmd_issued ();
WD->cmd_reg = WDC_FORMAT; /* format track */
busy_wait ();
while ( ( WD->status & WDS_SEEKDONE ) == 0 );
while ( ( WD->status & WDS_DRQ ) == 0 );
wrsec ( first.interleave , &WD->data , (LONG)HD_SECTOR );
wait_for_irq ();
deselect ();
return ( 0 );
}
deselect ()
{
busy_wait ();
WD->select = WD_DESELECT;
}
busy_wait ()
{
while ( WD->status & WDS_BUSY )
/*Delay ( 1L )*/;
}
bad_remap ( posn )
register struct posn *posn;
{
register int i;
if ( ! TestHashTable ( bad_hash_table , posn->block ) )
return;
for ( i = 0; i < first.bad_sectors; i++ ) {
if ( first.map[i] == posn->block ) {
posn->block = i + HD_MAP_SECTORS;
posn->sector = posn->block % first.sectors;
posn->surface = ( posn->block / first.sectors ) % first.heads;
posn->cylinder = posn->block / ( first.sectors * first.heads );
break;
}
}
}
/************************ Interrupt handling code ***********************/
/* change task that should get sent interrupt signals */
wd_subtask ( task )
struct Task *task;
{
data.task = task;
}
int
init_irq ()
{
/* set up data structure */
data.cmd_issued = 0;
data.task = FindTask ( 0L );
data.sig_bits = AllocSignal ( -1L );
if ( data.sig_bits < 0 ) {
return ( -1 );
}
data.sig_bits = 1L << data.sig_bits;
/* allocate interrupt structure node */
HDInterrupt = (struct Interrupt *)
AllocMem ( (LONG)sizeof ( struct Interrupt ) , (LONG)MEMF_PUBLIC );
if ( HDInterrupt == NULL ) {
return ( -1 );
}
/* set up interrupt structure flags */
HDInterrupt->is_Node.ln_Type = NT_INTERRUPT;
HDInterrupt->is_Node.ln_Pri = 0;
HDInterrupt->is_Node.ln_Name = "Hard Disk IRQ";
HDInterrupt->is_Data = (APTR) &data;
HDInterrupt->is_Code = (VOID(*)()) HDHandler;
AddIntServer ( (LONG)INTB_PORTS , HDInterrupt );
return ( 0 );
}
void
free_irq ()
{
if ( HDInterrupt != NULL ) {
RemIntServer ( (LONG)INTB_PORTS , HDInterrupt );
FreeMem ( HDInterrupt , (LONG)sizeof ( struct Interrupt ) );
HDInterrupt = NULL;
}
}
void
set_cmd_issued ()
{
/* clear spurious signals */
SetSignal ( (LONG)0 , data.sig_bits );
data.cmd_issued = 1;
}
void
wait_for_irq ()
{
Wait ( data.sig_bits );
}
LONG
HDHandler ()
{
if ( ! ( WD->status & WDS_BUSY ) ) {
if ( data.cmd_issued ) {
data.cmd_issued = 0;
Signal ( data.task , data.sig_bits );
return ( 1 );
}
}
return ( 0 );
}